//////////////////////////////////////
// Fast Container Library           //
// MIT License - Copyright (c) 2012 //
//////////////////////////////////////

#pragma once

#include "common.h"
#include "allocator.h"
#include "algobase.h"
#include "forward.h"

FC_NAMESPACE_BEGIN


template <class T, class Allocator>
class array_base
{
public:
	typedef array_base<T, Allocator>		this_type;
	typedef Allocator						allocator_type;

	array_base()
		: m_data(0), m_size(0), m_allocator()
		{
		}

	explicit array_base( const allocator_type& alloc )
		: m_data(0), m_size(0), m_allocator(alloc)
		{
		}

	array_base( const this_type& x )
		: m_data(0), m_size(0), m_allocator()
		{
			m_data = allocate_copy( x.m_size, x.begin(), x.end() );
			m_size = x.m_size;
		}

	~array_base()
		{
			fc::destroy_range( m_data, m_data + m_size );
			deallocate();
		}

	allocator_type& get_allocator() { return m_allocator; }
	const allocator_type& get_allocator() const { return m_allocator; }
	void set_allocator( allocator* alloc ) { m_allocator = alloc; }

	void fill( const T& value )
	{
		fc::fill_n( m_data, m_size, value );
	}

	void resize( size_t n, const T& value = T() )
	{
		if( FC_LIKELY(n != m_size) )
		{
			const size_t minSize = m_size < n ? m_size : n;
			T* newData = allocate_copy( n, m_data, m_data + minSize );
			fc::uninitialized_fill_n( newData + m_size, n - minSize, value );
			fc::destroy_range( m_data, m_data + m_size );
			deallocate();

			m_data = newData;
			m_size = n;
		}
	}

	template <class ForwardIterator>
	void assign( ForwardIterator first, ForwardIterator last )
	{
		const size_t n = (size_t)fc::distance(first, last);
		if( n != m_size )
		{
			T* newData = allocate_copy( n, first, last );
			fc::destroy_range( m_data, m_data + m_size );
			deallocate();
			m_data = newData;
			m_size = n;
		}
		else
		{
			fc::copy( first, last, m_data );
		}
	}

	this_type &operator =( const this_type &x )
	{
		if( this != &x )
			assign( x.m_data, x.m_data + x.m_size );

		return *this;
	}

	// this operation is O(1) only if allocators are comparable
	void swap( this_type& x )
	{
		if( m_allocator == x.get_allocator() )
		{
			fc::swap( m_data, x.m_data );
			fc::swap( m_size, x.m_size );
		}
		else
		{
			this_type temp(*this);
			*this = x;
			x = temp;
		}
	}

protected:
	T* allocate( size_t n )
	{
		return (T*)(n ? m_allocator.allocate( n * sizeof(T) ) : 0);
	}

	void deallocate()
	{
		if( m_data )
			m_allocator.deallocate( m_data, m_size * sizeof(T) );
	}

	template <class ForwardIterator>
	T* allocate_copy( size_t newCapacity, ForwardIterator first, ForwardIterator last )
	{
		T* p = allocate(newCapacity);
		fc::uninitialized_copy( first, last, p );
		return p;
	}

	T* allocate_fill( size_t newCapacity, const T& value )
	{
		T* p = allocate(newCapacity);
		fc::uninitialized_fill_n( p, newCapacity, value );
		return p;
	}

	T* m_data;
	size_t m_size;
	allocator_type m_allocator;
};




// global array_base operators

template <class T, class Allocator> inline
	void swap( const array_base<T, Allocator>& a, const array_base<T, Allocator>& b )
	{
		a.swap(b);
	}

template <class T, class A1, class A2> inline
	bool operator ==( const array_base<T, A1>& a, const array_base<T, A2>& b )
	{
		return( (void*)&a == (void*)&b || (a.size() == b.size() && fc::equal(a.begin(), a.end(), b.begin())) );
	}

template <class T, class A1, class A2> inline
	bool operator !=( const array_base<T, A1>& a, const array_base<T, A2>& b )
	{
		return !(a == b);
	}

template <class T, class A1, class A2> inline
	bool operator <( const array_base<T, A1>& a, const array_base<T, A2>& b )
	{
		return fc::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
	}

template <class T, class A1, class A2> inline
	bool operator >( const array_base<T, A1>& a, const array_base<T, A2>& b )
	{
		return b < a;
	}

template <class T, class A1, class A2> inline
	bool operator <=( const array_base<T, A1>& a, const array_base<T, A2>& b )
	{
		return !(b < a);
	}

template <class T, class A1, class A2> inline
	bool operator >=( const array_base<T, A1>& a, const array_base<T, A2>& b )
	{
		return !(a < b);
	}



FC_NAMESPACE_END
